type GetType<T> = T extends (arg: infer P) => void ? P : string

export function not (func: Function) {
  return function (...args: any[]) {
    return !func(...args)
  }
}
export function or (...funcs: Array<Function>) {
  return function (...args: any[]) {
    return funcs.some((i) => i(...args))
  }
}
export function and (...funcs: Array<Function>) {
  return function (...args: any[]) {
    return funcs.every((i) => i(...args))
  }
}
export function chain<T extends Function> (
  funcs: Array<T>,
  initial: GetType<T>
) {
  let ret = initial
  let stacks = funcs.slice()
  while (stacks.length) {
    let func = stacks.shift()
    ret = func!(initial)
  }
  return ret
}

export function valuable (i: any) {
  return !!i
}

export function flat<T> (arr: Array<Array<T>>) {
  return arr.reduce((prev, next) => [...prev, ...next], [])
}

export function identify<T> (key: keyof T) {
  return (t: T) => t[key]
}